// Entry point of the application.
$(function () {

    (function (model, ui, service, messages) {
        // Global variables declarations.
        var owners = new UserListController($('#searchOwnerTextBox'), $('#ownerListView')),
            delegates = new UserListController($('#searchDelegateTextBox'), $('#delegateListView')),
            dropZones = $('.dropZone'),
            ownerDropZone = $('#ownerDropZone'),
            delegateDropZone = $('#delegateDropZone'),
            commandPanel = $('#commandPanel'),
            addDelegationButton = $('#addDelegationButton'),
            removeDelegationButton = $('#removeDelegationButton'),
            cancelButton = $('#cancelButton'),
            messagePanel = $('#messagePanel');

        // "Class" to control a list view and its associated search text box.
        function UserListController(searchTextBox, listView, dropZone) {
            var _self = this,
                _model = null,
                _viewMap = {};

            // Model accessors.
            this.getModel = function () { return _model; }
            this.setModel = function (value) { _model = value; }

            // Bind the given model or default one to the UI, displaying the user views.
            this.bindModel = function (model) {
                _self.update(((model !== undefined) ? model : _model)
                .map(function (u) {
                    var uv = new ui.UserView(u)
                    return uv;
                }));
            }

            // Update the given user views or default ones.
            this.update = function (userViews) {
                _self.clear();
                ((userViews !== undefined) ? userViews : _views)
                    .each(function (uv) { _self.addUserView(uv); });
            }

            // Get a user view object given its id, allows to map a jquery object to a view object.
            this.getUserView = function (id) {
                return _viewMap[id];
            }

            // Get the current user views jquery object.
            this.getUserViews = function () {
                return listView.children();
            }

            // Iterates through the user view objects.
            this.eachUserView = function (proc) {
                for (var id in _viewMap)
                    proc(_viewMap[id]);
            }

            // Add a user view object.
            this.addUserView = function (view) {
                listView.append(view.toQObject());
                _viewMap[view.getId()] = view;
            }

            // Add a user view jquery object.
            this.add = function (viewQObj) {
                listView.prepend(viewQObj);
            }

            // Remove a user view jquery object.
            this.remove = function (viewQObj) {
                listView.remove(viewQObj);
            }

            // Remove all user views from the list.
            this.clear = function () {
                listView.html('');
                _viewMap = {};
            }

            // Hide the user views as well as the search box.
            this.hideUserViews = function () {
                listView.children().hide();
                searchTextBox.hide();
                searchTextBox.val('');
            }

            // Toggle the search box visibility when there are more than 4 views. 
            this.toggleSearchTextBox = function () {
                if (_self.getUserViews().length > 4)
                    searchTextBox.toggle('normal');
            }

            // Toggle the user views visibility as well as the search box.
            this.toggleUserViews = function (cont) {
                ui.fadeToggleItems(listView.children(), 8, 'normal', function () {
                    _self.toggleSearchTextBox();
                    if (cont) cont();
                });
            }

            // Enables search box search.
            // Shows user view whose model first name + last name or last name,
            // and sip starts by the typed value. 
            _self.searchAutocompletion = function (draggablesLazy, droppables) {
                searchTextBox.unbind('keyup');
                searchTextBox.keyup(function () {
                    var value = $(this).val().toLowerCase();

                    // Stopping drag and drop during the view update.
                    ui.stopDragAndDrop();
                    _self.bindModel(_model.filter(function (u) {
                        var name = (u.getFirstName() + ' ' + u.getLastName()).toLowerCase();
                        return name.startsWith(value) ||
                               u.getLastName().toLowerCase().startsWith(value) ||
                               u.getSip().toLowerCase().startsWith(value);
                    }));
                    ui.startDragAndDrop(draggablesLazy(), droppables);
                });
            }
        }

        // Subscribing to the "dragged" event to animate the take out from lists moves.
        dropZones.bind('dragged', function (e, dragged) {
            (delegates.getUserView(dragged.id) ? delegates : owners).remove($(dragged));
        });

        // Subscribing to the "droppedOut" event to put back user views in the list
        // when not droped in the dropping zones.
        dropZones.bind('droppedOut', function (e, dragged) {
            $(dragged).hide('slow', function () {
                (delegates.getUserView(dragged.id) ? delegates : owners).add($(this));
                $(this).css({ left: '0px', top: '0px' });
                $(this).show('normal');
            });
        });

        // Subscribing to the "droppedIn" of the "ownerZone" to notify a delegation
        // owner selection.
        ownerDropZone.bind('droppedIn', function (e, dragged) {
            onOwnerViewSelected(owners.getUserView(dragged.id));
        });

        // Subscribing to the "droppedIn" of the "delegateZone" to notify a delegation
        // delegate selection.
        delegateDropZone.bind('droppedIn', function (e, dragged) {
            var delegateView = delegates.getUserView(dragged.id);
            onDelegateViewSelected(delegateView || owners.getUserView(dragged.id), !!delegateView);
        });

        // Subscribing to the "click" of the "addDelegationButton" to start adding a
        // delegation between the selected users, then reseting the UI.
        addDelegationButton.click(function () {
            commandPanel.toggle('normal', function () {
                showMessage('addingDelegation');
                addDelegationAsync(owners.selectedView.getModel(), delegates.selectedView.getModel(), reset);
            });
        });

        // Subscribing to the "click" of the "removeDelegationButton" to start removing a
        // delegation between the selected users, then reseting the UI.
        removeDelegationButton.click(function () {
            commandPanel.toggle('normal', function () {
                showMessage('removingDelegation');
                removeDelegationAsync(owners.selectedView.getModel(), delegates.selectedView.getModel(), reset);
            });
        });

        // Subscribing to the "click" of the "cancelButton" to reset the UI.
        cancelButton.click(function () {
            commandPanel.toggle('normal', reset);
        });

        function showMessage(key) {
            if (!key) {
                messagePanel.text('');
                return;
            }
            messagePanel.fadeOut('slow', function () {
                messagePanel.text(messages.get(key));
                messagePanel.fadeIn('slow');
            });
        }

        // Initializes the UI.
        function initializeUI() {
            dropZones.hide();
            commandPanel.hide();
            owners.hideUserViews();
            delegates.hideUserViews();
        }

        // Initializes the UI from the given model.
        function initializeModel(users) {
            owners.defaultModel = users;

            owners.setModel(users);
            owners.bindModel();
            owners.hideUserViews();
            delegates.hideUserViews();


            owners.toggleUserViews(function () {
                ownerDropZone.slideToggle('slow', function () {
                    ui.startDragAndDrop(owners.getUserViews(), ownerDropZone);
                    owners.searchAutocompletion(function () { return owners.getUserViews() }, ownerDropZone);
                    showMessage('dropOwner');
                });
            });
        }

        // Resets the UI.
        function reset() {
            ownerDropZone.html('');
            delegateDropZone.html('');
            addDelegationButton.show();
            removeDelegationButton.show();
            showMessage(null);

            initializeUI();
            initializeModel(owners.defaultModel);
        }

        // Callback invoked when a owner user is selected.
        function onOwnerViewSelected(ownerView) {
            // Stopping drag and drop for the owner user views.
            ui.stopDragAndDrop();
            owners.selectedView = ownerView;
            var owner = owners.selectedView.getModel();

            // Loading the authorized delegates of the selected owner user.
            getAuthorizedDelegatesAsync(owner, function (users) {
                owners.setModel(users);
                owners.bindModel();
                owners.hideUserViews();

                owners.toggleUserViews(function () {

                    showMessage('listingDelegates');

                    // Loading the delegates of the selected owner user.
                    getDelegatesAsync(owner, function (users) {
                        delegates.setModel(users);
                        delegates.bindModel();
                        delegates.hideUserViews();

                        // Removing the owner user delegates views from the owner user views,
                        // preventing a user to add existing delegates to himself.
                        owners.eachUserView(function (uv) {
                            var delegateUsers = delegates.getModel();

                            for (var i = 0; i < delegateUsers.length; ++i)
                                if (delegateUsers[i].getSip() == uv.getModel().getSip()) {
                                    var qobj = uv.toQObject();
                                    qobj.fadeToggle('normal', function () { qobj.remove() });
                                }
                        });

                        // Enabling the delegates list view.
                        delegates.toggleUserViews(function () {
                            delegateDropZone.slideToggle('slow', function () {
                                ui.startDragAndDrop(owners.getUserViews().add(delegates.getUserViews()), delegateDropZone);
                                delegates.searchAutocompletion(function () {
                                    return owners.getUserViews().add(delegates.getUserViews());
                                }, delegateDropZone);
                                showMessage('dropDelegate');
                            });
                        });
                    });
                });
            });
        }

        // Callback invoked when a delegate user is selected.
        function onDelegateViewSelected(delegateView, isDelegate) {
            ui.stopDragAndDrop();
            owners.toggleSearchTextBox();
            delegates.toggleSearchTextBox();

            delegates.selectedView = delegateView;
            (isDelegate ? addDelegationButton : removeDelegationButton).hide();
            showMessage(null);

            commandPanel.slideToggle('slow', function () {
                showMessage('chooseAction');
            });
        }


        // Actions

        var _service = new service.LyncServiceProxy(messages.getLanguage());

        // Function to load authorized owners from the web service.
        function getAuthorizedOwnersAsync(cont) {
            _service.listAuthorizedOwners(cont, onError);
        }

        // Function to load a user authorized delegates from the web service.
        function getAuthorizedDelegatesAsync(owner, cont) {
            _service.listAuthorizedDelegates(owner.getSip(), cont, onError);
        }

        // Function to load a user delegates from the web service.
        function getDelegatesAsync(owner, cont) {
            _service.listDelegates(owner.getSip(), cont, onError);
        }

        // Function to add a delegation between owner and delegate. 
        function addDelegationAsync(owner, delegate, cont) {
            _service.setCallForwardingDelegation(true, owner.getSip(), delegate.getSip(), function () {
                if (cont) cont();
            }, onError);
        }

        // Function to remove a delegation between owner and delegate. 
        function removeDelegationAsync(owner, delegate, cont) {
            _service.setCallForwardingDelegation(false, owner.getSip(), delegate.getSip(), function () {
                if (cont) cont();
            }, onError);
        }

        // Callback invoked when an error occurs with the service.
        function onError(message) {
            window.alert(message);
        }

        // Starting the web application by initializing the UI and the model.
        initializeUI();
        getAuthorizedOwnersAsync(initializeModel);

    })(LyncServerExtension.Model, LyncServerExtension.UI, LyncServerExtension.Service, LyncServerExtension.Messages);
});